home *** CD-ROM | disk | FTP | other *** search
/ Magnum One / Magnum One (Mid-American Digital) (Disc Manufacturing).iso / d16 / winvn060.arc / WVLIST.C < prev    next >
C/C++ Source or Header  |  1991-07-01  |  18KB  |  620 lines

  1. /*-- WVLIST.C -- File containing functions to deal with the NNTP LIST
  2.  *   command, which lists all the newsgroups and their status.
  3.  *
  4.  *   Mark Riordan   25 October 1990
  5.  */
  6.  
  7. #include "windows.h"
  8. #include "wvglob.h"
  9. #include "winvn.h"
  10. #ifndef MAC
  11. #include "winundoc.h"
  12. #include <ctype.h>
  13. #endif
  14.  
  15.  
  16. #define HASHMAX 9721
  17. #define MAXGROUPS  3200
  18.  
  19. TypLine far * far * NetHashTable;
  20. HANDLE hNetHashTable;
  21.  
  22. HANDLE htohNewGroupLines;
  23. HANDLE far *lphNewGroupLines;  /* array of handles to new group lines */
  24.  
  25.  
  26. HWND hDlgList;  /* Handle to child list box window. */
  27.  
  28. #define MyGlobalUnlock(hWhat)  MRRGlobalUnlock(hWhat,__LINE__)
  29.  
  30. void MRRGlobalUnlock(HANDLE hWhat,WORD wLine);
  31.  
  32. void
  33. MRRGlobalUnlock(hWhat,wLine)
  34. HANDLE hWhat;
  35. WORD wLine;
  36. {
  37.    WORD LockCount;
  38.    char mybuf[80];
  39.  
  40.    if(!(LockCount = (GMEM_LOCKCOUNT & GlobalFlags(hWhat)))) {
  41. #if 0
  42.       sprintf(mybuf,"In WVLIST.C line %d",wLine);
  43.       MessageBox(hWndConf,mybuf,"Attempt to unlock 0 LockCount",MB_OK);
  44. #endif
  45.    } else {
  46.       GlobalUnlock(hWhat);
  47.    }
  48.  
  49. }
  50.  
  51. /*--- function StartList -----------------------------------------------
  52.  *
  53.  *  Initiate the process of sending a LIST command and using its
  54.  *  output to update our list of news groups.
  55.  */
  56.  
  57. void
  58. StartList()
  59. {
  60.    unsigned int hashval;
  61.  
  62.    CommState = ST_LIST_RESP;
  63.    CommBusy = TRUE;
  64.    PutCommLine("LIST",4);
  65.    Initializing = INIT_SCANNING_NETDOC;
  66.    InvalidateRect(hWndConf,NULL,FALSE);
  67.    SendMessage(hWndConf,WM_PAINT,0,0L);
  68.  
  69.    /* Set up hash table for group names */
  70.  
  71.    hNetHashTable = GlobalAlloc(GMEM_MOVEABLE,(long)HASHMAX * sizeof(TypLine far *));
  72.    NetHashTable = (TypLine far * far *) GlobalLock(hNetHashTable);
  73.    for(hashval=0; hashval<HASHMAX; hashval++) {
  74.       NetHashTable[hashval] = (TypLine far *) 0;
  75.    }
  76.  
  77.    HashNetGroups(&NetDoc,NetHashTable);
  78.    InvalidateRect(hWndConf,NULL,FALSE);
  79.    Initializing = INIT_GETTING_LIST;
  80.    SendMessage(hWndConf,WM_PAINT,0,0L);
  81.  
  82.    /* Set up table of pointers to new group lines.
  83.     */
  84. #if 0
  85.    htohNewGroupLines = GlobalAlloc(GMEM_MOVEABLE,(long)MAXGROUPS * sizeof(HANDLE));
  86.    lphNewGroupLines = (HANDLE far *) GlobalLock(htohNewGroupLines);
  87. #endif
  88.  
  89.    InitGroupTable();
  90. }
  91.  
  92. /*--- function InitGroupTable -------------------------------------------
  93.  *
  94.  *  Allocate the NewGroupTable.
  95.  *
  96.  *  Exit:   hNewGroupTable    is the handle
  97.  *          NewGroupTable     points to the table
  98.  *          nNewGroups        has been initialized to 0.
  99.  */
  100. void
  101. InitGroupTable(void)
  102. {
  103.    hNewGroupTable = GlobalAlloc(GMEM_MOVEABLE,(long)MAXGROUPS * sizeof(TypLine far *));
  104.    NewGroupTable = (void far * far *) GlobalLock(hNewGroupTable);
  105.  
  106.    nNewGroups = 0;
  107. }
  108.  
  109.  
  110. /*--- function HashNetGroups ------------------------------------------
  111.  *
  112.  *  Enter all the groups in the Net document into the hash table.
  113.  *
  114.  *  Exit    All blocks in the document are locked.
  115.  *          HashTable   is (partially) filled with pointers to
  116.  *                      each of the lines in "Doc".
  117.  */
  118.  
  119. void
  120. HashNetGroups(Doc,HashTable)
  121. TypDoc *Doc;
  122. TypLine far * far * HashTable;
  123. {
  124.    TypBlock far *BlockPtr;
  125.    TypLine  far *LinePtr;
  126.    TypLine far * far *hashptr;
  127.    unsigned int hashval;
  128.    HANDLE  hBlock;
  129.    unsigned int Offset;
  130.    unsigned int TextOffset;
  131.    unsigned char far *textptr;
  132.    TypLineID MyLineID;
  133.  
  134.    /* Lock all blocks in the document */
  135.    hBlock = Doc->hFirstBlock;
  136.    do {
  137.       BlockPtr = (TypBlock far *) GlobalLock(hBlock);
  138.       hBlock = BlockPtr->hNextBlock;
  139.    } while(hBlock);
  140.  
  141.  
  142.    /* Now start at the beginning of the document, going through
  143.     * each line in the document, hashing its group name into the table.
  144.     */
  145.  
  146.    hBlock = Doc->hFirstBlock;
  147.    Offset = sizeof(TypBlock);
  148.    MyLineID = 0L;
  149.  
  150.    LockLine(hBlock,Offset,MyLineID,&BlockPtr,&LinePtr);
  151.  
  152.    TextOffset = Doc->OffsetToText;
  153.  
  154.    if(LinePtr->length != END_OF_BLOCK) {
  155.       do {
  156.          textptr=((unsigned char far *)LinePtr)+TextOffset;
  157.          hashval = HashGroup(textptr);
  158.          while(HashTable[hashval]) {
  159.             hashval = (hashval+1) % HASHMAX;
  160.          }
  161.          HashTable[hashval] = LinePtr;
  162.       } while(NextLine(&BlockPtr,&LinePtr));
  163.    }
  164.    UnlockLine(BlockPtr,LinePtr,&hBlock,&Offset,&MyLineID);
  165. }
  166.  
  167.  
  168. /*--- function ProcListLine ---------------------------------------------
  169.  *
  170.  *  Process a line received from the NNTP LIST command output.
  171.  *  Each line from the LIST command has the form:
  172.  *   <groupname> <highest_art_#> <lowest_art_#> {y|n|m}
  173.  */
  174.  
  175. void
  176. ProcListLine(ListLine)
  177. unsigned char *ListLine;
  178. {
  179.    unsigned int hashval;
  180.    char far *textptr;
  181.    unsigned char *cptr, *restline;
  182.    char mygroupline[BLOCK_SIZE];
  183.    TypGroup *mygroup;
  184.    TypGroup far *netgroup;
  185.    long int ArtNum;
  186.  
  187.    if((++RcvLineCount)%UPDATE_TITLE_FREQ == 0) {
  188.       InvalidateRect(hWndConf,NULL,FALSE);
  189.       if(RcvLineCount%(UPDATE_TITLE_FREQ*25) == 0) {
  190.          UpdateWindow(hWndConf);
  191.       }
  192.    }
  193.  
  194.    /* Replace the first blank in the input line with a zero. */
  195.    for(cptr=ListLine; *cptr && *cptr!=' '; cptr++);
  196.    *cptr = '\0';
  197.    restline = cptr+1;   /* points to highest art # */
  198.  
  199.    hashval = HashGroup(ListLine);
  200. checkhash:;
  201.    if(!NetHashTable[hashval]) {
  202.       /* This is a new group.
  203.        * Create a Group line from the information in this line.
  204.        */
  205.       CrackGroupLine(ListLine,(TypLine *)(mygroupline));
  206.       mygroup = (TypGroup *) (mygroupline+sizeof(TypLine));
  207.       GetNum(&restline,&(mygroup->ServerLast));
  208.       GetNum(&restline,&(mygroup->ServerFirst));
  209.       mygroup->HighestPrevSeen = 0;
  210.  
  211.       AddGroupToTable(mygroupline);
  212. #if 0
  213.       MessageBox(hWndConf,ListLine,"New Group:",MB_OK);
  214. #endif
  215.    } else {
  216.       textptr = ((char far *) NetHashTable[hashval]+sizeof(TypLine)+sizeof(TypGroup));
  217.       if(lstrcmp(textptr,ListLine)) {
  218.          hashval = (hashval+1) % HASHMAX;
  219.          goto checkhash;
  220.       } else {
  221.          /* This group is already present in NetDoc.
  222.           * Update the ServerFirst and ServerLast fields.
  223.           */
  224.          netgroup = (TypGroup far *)((char far *) NetHashTable[hashval]+sizeof(TypLine));
  225.          GetNum(&restline,&ArtNum);
  226.          netgroup->ServerLast = ArtNum;
  227.          GetNum(&restline,&ArtNum);
  228.          netgroup->ServerFirst = ArtNum;
  229.        }
  230.    }
  231.  
  232. }
  233.  
  234. /*--- function AddGroupToTable ----------------------------------------
  235.  *
  236.  *  Add a group line, formatted for eventual inclusion in NetDoc,
  237.  *  to NewGroupTable.
  238.  *
  239.  *  Entry:  GroupLine      is the line to add to the table.
  240.  *
  241.  *  Exit:   NewGroupTable  contains the line
  242.  *          nNewGroups     has been incremented.
  243.  */
  244. void
  245. AddGroupToTable(char far *GroupLine)
  246. {
  247.    HANDLE hLine;
  248.    char far *AllocPtr;
  249.    int mylength;
  250.  
  251.       /* Create a copy of this line in far memory and place pointers
  252.        * to in our arrays.
  253.        */
  254.  
  255.       mylength = ((TypLine far *)GroupLine)->length + sizeof(HANDLE);
  256.       hLine = GlobalAlloc(GMEM_MOVEABLE,(long)mylength);
  257.       AllocPtr = (char far *) GlobalLock(hLine);
  258.       MoveBytes((char far *)&hLine, AllocPtr, sizeof(hLine));
  259.       MoveBytes((char far *)GroupLine,AllocPtr+sizeof(hLine),mylength-sizeof(hLine));
  260.  
  261.       NewGroupTable[nNewGroups] = AllocPtr;
  262.       nNewGroups++;
  263. }
  264.  
  265. /*--- function HashGroup -------------------------------------------------
  266.  *
  267.  *  Hash a string into an unsigned integer.
  268.  *
  269.  *  This hash function is designed based on information from a
  270.  *  handout for a UC Berkeley class CS 60C, Spring 1990, Clancy/
  271.  *  Harrison.  I picked up the handout while wandering around on
  272.  *  Berkeley's campus in May 1990.  The handout in turn is based
  273.  *  on the McKenzie, et al., article "Selecting a Hashing Algorithm"
  274.  *  in Software Practice and Experience, Vol 20, no 2, Feb 1990.
  275.  *  The algorithm is similar to that used in the AT&T C++ compiler.
  276.  *  /mrr
  277.  */
  278.  
  279. unsigned int
  280. HashGroup(gname)
  281. unsigned char far *gname;
  282. {
  283.    long unsigned int sum = 0;
  284.    unsigned int hash;
  285.  
  286.    for(;*gname; gname++) {
  287.       sum = (sum<<1) + *gname;
  288.    }
  289.    hash = sum % HASHMAX;
  290.    return(hash);
  291. }
  292.  
  293.  
  294. /*--- function ProcEndList -------------------------------------------
  295.  *
  296.  *  Do the final processing when we have reached the end of the
  297.  *  list of newsgroups sent us via the LIST command.
  298.  */
  299.  
  300. void
  301. ProcEndList()
  302. {
  303.    char mybuf[80];
  304.    char far *cptr;
  305.    int j, selstate;
  306.    FARPROC lpfnWinVnGroupListDlg;
  307.    TypBlock far *BlockPtr;
  308.    TypLine far *LinePtr;
  309.    TypGroup far *group;
  310.    WORD LockCount;
  311.  
  312. #if 0
  313.    sprintf(mybuf,"%d new groups found.",nNewGroups);
  314.    MessageBox(hWndConf,mybuf,"",MB_OK);
  315. #endif
  316.  
  317.    ShellSort(NewGroupTable,nNewGroups,sizeof(void far *),GroupCompare);
  318.  
  319. #if 0
  320.    for(j=0; j<nNewGroups; j++) {
  321.       cptr = ((char far *)NewGroupTable[j]) + sizeof(HANDLE) + sizeof(TypLine) + sizeof(TypGroup);
  322.       MessageBox(hWndConf,cptr,"New Group:",MB_OK);
  323.    }
  324. #endif
  325.    lpfnWinVnGroupListDlg =  MakeProcInstance(WinVnGroupListDlg,hInst);
  326.  
  327.    /* Display dialog box of new groups.
  328.     */
  329.    if(nNewGroups && DialogBox(hInst,"WinVnGroupList",hWndConf,lpfnWinVnGroupListDlg)) {
  330.       /* The user has clicked OK, so add all the new groups to the
  331.        * Net document.  Subscribed groups go at the end of the Subscribed
  332.        * section at the top of the doc.
  333.        * Unsubscribed groups go in the section below, in alphabetical order.
  334.        */
  335.       MergeGroups(ADD_SUBSCRIBED_END_OF_SUB);
  336.    }
  337.  
  338.    /* Unlock and/or free memory in NetDoc and NewGroupTable.  */
  339.  
  340.    CleanUpGroupTable();
  341.  
  342.    /* Unlock and free the hash table.   */
  343.  
  344.    LockCount = GMEM_LOCKCOUNT & GlobalFlags(hNetHashTable);
  345.    MyGlobalUnlock(hNetHashTable);
  346.    GlobalFree(hNetHashTable);
  347.  
  348.    InvalidateRect(hWndConf,NULL,FALSE);
  349.    SetNetDocTitle();
  350. }
  351.  
  352. /*--- function GroupCompare --------------------------------------------
  353.  *
  354.  *  Compare two group lines alphabetically by group name.
  355.  */
  356. int
  357. GroupCompare(g1,g2)
  358. TypLine far * far *g1;
  359. TypLine far * far *g2;
  360. {
  361.    char far *gch1, far *gch2;
  362.  
  363.    gch1 = (char far *)*g1 + sizeof(HANDLE) + sizeof(TypLine) + sizeof(TypGroup);
  364.    gch2 = (char far *)*g2 + sizeof(HANDLE) + sizeof(TypLine) + sizeof(TypGroup);
  365.  
  366.    return(lstrcmp(gch1,gch2));
  367. }
  368.  
  369.  
  370. /*-- function WinVnGroupListDlg ---------------------------------------
  371.  *
  372.  *  Dialog function to handle selection of new newsgroups to
  373.  *  subscribe to.  (I know, don't end a sentence with "to".)
  374.  */
  375.  
  376. BOOL FAR PASCAL WinVnGroupListDlg(hDlg, iMessage, wParam, lParam)
  377. HWND      hDlg;
  378. unsigned  iMessage;
  379. WORD      wParam;
  380. LONG      lParam;
  381. {
  382.    int j, selstate;
  383.    char far *cptr;
  384.    WORD notification;
  385.    int wItem;
  386.    void far *AllocPtr;
  387.    TypGroup far *group;
  388.  
  389.  
  390.    switch(iMessage) {
  391.  
  392.       case WM_INITDIALOG:
  393.          hDlgList = GetDlgItem(hDlg,IDD_GROUP_LISTBOX);
  394.  
  395.          for(j=0; j<nNewGroups; j++) {
  396.             cptr = 0;
  397.             cptr = (NewGroupTable[j]);
  398.             cptr += sizeof(HANDLE) + sizeof(TypLine) + sizeof(TypGroup);
  399.             SendMessage(hDlgList,LB_ADDSTRING,-1,(LONG) cptr);
  400.          }
  401.          SendMessage(hDlgList,WM_SETREDRAW,TRUE,0L);
  402.          return TRUE;
  403.          break;
  404.  
  405.       case WM_COMMAND:
  406.          switch(wParam) {
  407.             case IDOK:
  408.                EndDialog(hDlg,TRUE);
  409.                break;
  410.  
  411.             case IDCANCEL:
  412.                EndDialog(hDlg,FALSE);
  413.                break;
  414. #if 0
  415.             case IDD_GROUP_LISTBOX:
  416.                notification = HIWORD(lParam);
  417.                if(notification == LBN_SELCHANGE) {
  418.                   wItem = (WORD) SendMessage(hDlgList,LB_GETCURSEL,0,0L);
  419.  
  420.                }
  421.                break;
  422. #endif
  423.             default:
  424.                return FALSE;
  425.          }
  426.          break;
  427.  
  428.       case WM_DESTROY:
  429.          /* Mark the groups pointed to by NewGroupTable as Subscribed
  430.           * or not, depending upon whether they are now selected.
  431.           */
  432.          for(j=0; j<nNewGroups; j++) {
  433.             selstate = (WORD) SendMessage(hDlgList,LB_GETSEL,j,0L);
  434.             AllocPtr = (char far *)NewGroupTable[j] + sizeof(HANDLE);
  435.             group = (TypGroup far *) ((char far *)AllocPtr + sizeof(TypLine));
  436.             group->Subscribed = selstate;
  437.          }
  438.          break;
  439.  
  440.       default:
  441.          return FALSE;
  442.          break;
  443.    }
  444.    return TRUE;
  445. }
  446.  
  447. /*--- function PositionEndSubscribed ----------------------------------
  448.  *
  449.  *  Position a pointer to the end of the subscribed section at the
  450.  *  beginning of the net document.
  451.  *
  452.  *  Entry   None
  453.  *
  454.  *  Exit    BlockPtr and LinePtr  point to the place in NetDoc just
  455.  *            beyond the last subscribed group.  We assume that
  456.  *            all subscribed groups go at the beginning of the
  457.  *            document.
  458.  */
  459. void
  460. PositionEndSubscribed(TypBlock far **BlockPtr, TypLine far **LinePtr)
  461. {
  462.    BOOL advance;
  463.    TypGroup far *group;
  464.  
  465.    TopOfDoc(&NetDoc,BlockPtr,LinePtr);
  466.    advance = TRUE;
  467.    do {
  468.       group = (TypGroup far *)((char far *)*LinePtr+sizeof(TypLine));
  469.       if(group->Subscribed) {
  470.          advance = NextLine(BlockPtr,LinePtr);
  471.       } else {
  472.          advance = FALSE;
  473.       }
  474.    } while(advance);
  475. }
  476.  
  477. /*--- function MergeGroups ----------------------------------------
  478.  *
  479.  *  Merge a list of groups into NetDoc.
  480.  *
  481.  *  Entry:  NewGroupTable  is an array of pointers to TypGroup structures
  482.  *                         of groups to be merged into NetDoc.
  483.  *          hNewGroupTable is the handle to the above.
  484.  *          nNewGroups     is the number of groups in the table.
  485.  *          WhereSubscribed   indicates where new subscribed groups
  486.  *                         should be added.
  487.  *                         ADD_SUBSCRIBED_END_OF_SUB indicates that
  488.  *                           they should be added at the end of the subscribed
  489.  *                           list, before the unsubscribed groups.
  490.  *                         ADD_SUBSCRIBED_TOP_OF_DOC indicates that they
  491.  *                           should be added at the top of the document.
  492.  *  Exit:   The groups in the table have been added to NetDoc, and
  493.  *            the entries in GroupTable have been freed from memory.
  494.  *            Also, GroupTable itself has been freed.
  495.  */
  496. void
  497. MergeGroups(int WhereSubscribed)
  498. {
  499.    TypBlock far *BlockPtr;
  500.    TypLine far *LinePtr;
  501.    TypGroup far *group;
  502.    char far *netcptr;
  503.    char far *grpcptr;
  504.    void far *AllocPtr;
  505.    HANDLE hLine;
  506.    unsigned int Offset;
  507.    TypLineID MyLineID;
  508.    char myline[BLOCK_SIZE];
  509.    int j, advance;
  510.  
  511.    switch(WhereSubscribed) {
  512.       case ADD_SUBSCRIBED_END_OF_SUB:
  513.          PositionEndSubscribed(&BlockPtr,&LinePtr);
  514.          break;
  515.       case ADD_SUBSCRIBED_TOP_OF_DOC:
  516.          TopOfDoc(&NetDoc,&BlockPtr,&LinePtr);
  517.          break;
  518.    }
  519.  
  520.       /* BlockPtr and LinePtr point to the
  521.        * place to add new subscribed groups.
  522.        * Loop through the new groups; for subscribed groups, add
  523.        * them to NetDoc at this point.
  524.        * For each subscribed group, unlock and free the corresponding
  525.        * line pointed to by NewGroupTable.  Set the table entry
  526.        * to 0 to indicate that this group has been dealt with.
  527.        */
  528.  
  529.       for(j=0; j<nNewGroups; j++) {
  530.          AllocPtr = (char far *)NewGroupTable[j] + sizeof(HANDLE);
  531.          group = (TypGroup far *) ((char far *)AllocPtr + sizeof(TypLine));
  532.          if(group->Subscribed) {
  533.             /* This group has been selected and should be subscribed to.
  534.              */
  535.             MoveBytes(AllocPtr,myline,((TypLine far *)AllocPtr)->length);
  536.             AddLine((TypLine *)myline,&BlockPtr,&LinePtr);
  537.             NetDoc.ActiveLines++;
  538.             hLine = *((HANDLE far *) NewGroupTable[j]);
  539.             MyGlobalUnlock(hLine);
  540.             GlobalFree(hLine);
  541.             NewGroupTable[j] = (void far *) 0;
  542.          }
  543.       }
  544.  
  545.       PositionEndSubscribed(&BlockPtr,&LinePtr);
  546.  
  547.       /* Now take a second pass through NewGroupTable, for the
  548.        * unsubscribed groups.  If NewGroupTable[j] is non-zero, then
  549.        * that group should be entered into the second, unsubscribed
  550.        * section of NetDoc, merged in in alphabetical order.
  551.        *
  552.        * BlockPtr and LinePtr point to the first unsubscribed group.
  553.        */
  554.  
  555.       for(j=0; j<nNewGroups; j++) {
  556.          if(NewGroupTable[j]) {
  557.             /* Search for the right place to add this line. */
  558.  
  559.             AllocPtr = (char far *)NewGroupTable[j] + sizeof(HANDLE);
  560.             grpcptr = ((char far *)AllocPtr)+sizeof(TypLine)+sizeof(TypGroup);
  561.             advance = TRUE;
  562.             do {
  563.                netcptr = ((char far *)LinePtr)+sizeof(TypLine)+sizeof(TypGroup);
  564.                if(lstrcmp(grpcptr,netcptr) < 0) {
  565.                   advance = FALSE;
  566.                } else {
  567.                   advance = NextLine(&BlockPtr,&LinePtr);
  568.                }
  569.             } while (advance);
  570.  
  571.             /* Now add the new group at this point */
  572.             MoveBytes(AllocPtr,myline,((TypLine far *)AllocPtr)->length);
  573.             AddLine((TypLine *)myline,&BlockPtr,&LinePtr);
  574.  
  575.             /* Unlock and free this entry in NewGroupTable. */
  576.             hLine = *((HANDLE far *) NewGroupTable[j]);
  577.             MyGlobalUnlock(hLine);
  578.             GlobalFree(hLine);
  579.          }
  580.       }
  581.  
  582.       UnlockLine(BlockPtr,LinePtr,&hLine,&Offset,&MyLineID);
  583. }
  584.  
  585. /*--- function CleanUpGroupTable ------------------------------------
  586.  *
  587.  *  Clean up after doing processing to add or move groups in NetDoc.
  588.  */
  589. void
  590. CleanUpGroupTable()
  591. {
  592.    HANDLE hBlock, hBlockNext;
  593.    TypBlock far *BlockPtr;
  594.  
  595.    /* Unlock all blocks in the NetDoc document.
  596.     */
  597.  
  598.    hBlock = NetDoc.hFirstBlock;
  599.    do {
  600.       BlockPtr = (TypBlock far *) GlobalLock(hBlock);
  601.       hBlockNext = BlockPtr->hNextBlock;
  602.  
  603.       MyGlobalUnlock(hBlock);
  604. #if 0
  605.       LockCount = GMEM_LOCKCOUNT & GlobalFlags(hBlock);
  606. #endif
  607.       MyGlobalUnlock(hBlock);
  608.       hBlock = hBlockNext;
  609.    } while(hBlock);
  610.  
  611.    /* Unlock and free the NewGroupTable itself.  */
  612.  
  613. #if 0
  614.    LockCount = GMEM_LOCKCOUNT & GlobalFlags(hNewGroupTable);
  615. #endif
  616.    MyGlobalUnlock(hNewGroupTable);
  617.    GlobalFree(hNewGroupTable);
  618.  
  619. }
  620.